From: Caolán McNamara Date: Mon, 13 Jul 2026 22:42:07 +0000 (+0000) Subject: [PATCH] put media files under link update control X-Git-Tag: archive/raspbian/4%25.2.3-2+rpi1+deb13u8^2~14 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/%22/%22http:/www.example.com/cgi/%22?a=commitdiff_plain;h=dc52604e68bcdbc7e1ab6523b1cd413a9f4ef037;p=libreoffice.git [PATCH] put media files under link update control A media object whose content lives at an external URL, rather than inside the document, is registered with the link manager like a linked graphic. So it then shares the usual link update mechanism. Media copied into the document is extracted to a temp file and is not a link, so that's left alone. Change-Id: I59dadaf05567ee9d9c0492b13cbc7174aca953da Signed-off-by: Caolán McNamara Reviewed-on: https://gerrit.collaboraoffice.com/c/core/+/6980 Tested-by: Jenkins CPCI Reviewed-by: Miklos Vajna (cherry picked from commit 1ea4fc54036587c7eb4d4cea74e776de9e6baaf3) Signed-off-by: Xisco Fauli Reviewed-on: https://gerrit.libreoffice.org/c/core/+/208393 Reviewed-by: Hossein Tested-by: Ilmari Lauhakangas Reviewed-by: Ilmari Lauhakangas Gbp-Pq: Name CVE-2026-63269-2.diff --- diff --git a/include/svx/svdomedia.hxx b/include/svx/svdomedia.hxx index b1c5959f4b9..ad1c6ba3604 100644 --- a/include/svx/svdomedia.hxx +++ b/include/svx/svdomedia.hxx @@ -29,10 +29,12 @@ class Graphic; namespace sdr::contact { class ViewContactOfSdrMediaObj; } namespace com::sun::star::graphic { class XGraphic; } +class SdrMediaLink; class SVXCORE_DLLPUBLIC SdrMediaObj final : public SdrRectObj { friend class sdr::contact::ViewContactOfSdrMediaObj; + friend class SdrMediaLink; private: // protected destructor - due to final, make private @@ -78,6 +80,17 @@ public: private: SAL_DLLPRIVATE void mediaPropertiesChanged( const ::avmedia::MediaItem& rNewState ); SAL_DLLPRIVATE virtual std::unique_ptr CreateObjectSpecificViewContact() override; + SAL_DLLPRIVATE virtual void handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage) override; + + // Register an external media reference as a document link, so it takes + // part in the normal link update permission just like a linked graphic. + // Media stored inside the document is not a link and is left alone. + SAL_DLLPRIVATE void ImpRegisterLink(); + SAL_DLLPRIVATE void ImpDeregisterLink(); + + // Fetch a snapshot frame from the media URL. The caller decides + // whether a fetch is allowed. + SAL_DLLPRIVATE void grabSnapshot(const OUString& rRealURL) const; struct Impl; std::unique_ptr m_xImpl; diff --git a/sd/source/ui/docshell/docshel4.cxx b/sd/source/ui/docshell/docshel4.cxx index dca885bcd2c..f615de240aa 100644 --- a/sd/source/ui/docshell/docshel4.cxx +++ b/sd/source/ui/docshell/docshel4.cxx @@ -452,6 +452,8 @@ bool DrawDocShell::ImportFrom(SfxMedium &rMedium, } else // initial loading of the document { + comphelper::EmbeddedObjectContainer& rEmbeddedObjectContainer = getEmbeddedObjectContainer(); + rEmbeddedObjectContainer.setUserAllowsLinkUpdate(false); mpDoc->EnableUndo(false); } diff --git a/svx/qa/unit/data/media-link.fodp b/svx/qa/unit/data/media-link.fodp new file mode 100644 index 00000000000..02f0fef974e --- /dev/null +++ b/svx/qa/unit/data/media-link.fodp @@ -0,0 +1,19 @@ + + + + + + + + + + + + diff --git a/svx/qa/unit/sdr.cxx b/svx/qa/unit/sdr.cxx index 3b7139acb3e..7352bb0ef76 100644 --- a/svx/qa/unit/sdr.cxx +++ b/svx/qa/unit/sdr.cxx @@ -7,6 +7,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include + #include #include @@ -14,9 +16,11 @@ #include #include +#include #include #include #include +#include #include #include #include @@ -189,6 +193,38 @@ CPPUNIT_TEST_FIXTURE(SdrTest, testSlideBackground) // i.e. the rendering did not find the bitmap. assertXPath(pDocument, "//bitmap", 1); } + +CPPUNIT_TEST_FIXTURE(SdrTest, testMediaLinkNotFetchedWhenUpdatesDisallowed) +{ + // A media object referenced by an external xlink:href, with no copy of + // the media stored inside the document. + loadFromFile(u"media-link.fodp"); + uno::Reference xDrawPagesSupplier(mxComponent, uno::UNO_QUERY); + uno::Reference xDrawPage(xDrawPagesSupplier->getDrawPages()->getByIndex(0), + uno::UNO_QUERY); + auto* pSvxDrawPage = dynamic_cast(xDrawPage.get()); + CPPUNIT_ASSERT(pSvxDrawPage); + auto* pMedia = dynamic_cast(pSvxDrawPage->GetSdrPage()->GetObj(0)); + CPPUNIT_ASSERT(pMedia); + + // With link updates disallowed, as when loading or converting a document + // without a user present to approve them, asking for the placeholder image + // must not reach out to the external URL. Without the fix the snapshot is + // generated (an empty placeholder bitmap even where no media backend is built, + // and the referenced content where one is), so the returned graphic is set. + SfxObjectShell* pShell = SfxObjectShell::GetShellFromComponent(mxComponent); + CPPUNIT_ASSERT(pShell); + pShell->getEmbeddedObjectContainer().setUserAllowsLinkUpdate(false); + CPPUNIT_ASSERT(!pMedia->getSnapshot().is()); + +#if HAVE_FEATURE_AVMEDIA + // Once updates are allowed the same request produces a graphic, so it was + // the permission alone that blocked the fetch above. + pShell->getEmbeddedObjectContainer().setUserAllowsLinkUpdate(true); + CPPUNIT_ASSERT(pMedia->getSnapshot().is()); +#endif +} + } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svx/source/svdraw/svdomedia.cxx b/svx/source/svdraw/svdomedia.cxx index 2f702adba3d..32603bd7663 100644 --- a/svx/source/svdraw/svdomedia.cxx +++ b/svx/source/svdraw/svdomedia.cxx @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -38,10 +39,29 @@ #include #include #include +#include +#include +#include +#include using namespace ::com::sun::star; +// A media object that points at an external URL is a document link. Registering +// it with the link manager lets it share the normal link update permission: the +// snapshot is only fetched from the URL once the user has allowed link updates. +class SdrMediaLink final : public sfx2::SvBaseLink +{ + SdrMediaObj& mrMediaObj; + +public: + explicit SdrMediaLink(SdrMediaObj& rObj); + + virtual void Closed() override; + virtual ::sfx2::SvBaseLink::UpdateResult DataChanged( + const OUString& rMimeType, const css::uno::Any& rValue) override; +}; + struct SdrMediaObj::Impl { ::avmedia::MediaItem m_MediaProperties; @@ -54,8 +74,41 @@ struct SdrMediaObj::Impl uno::Reference< graphic::XGraphic > m_xCachedSnapshot; rtl::Reference m_xPlayerListener; OUString m_LastFailedPkgURL; + // owned by the link manager while registered, null otherwise + SdrMediaLink* m_pMediaLink = nullptr; }; +SdrMediaLink::SdrMediaLink(SdrMediaObj& rObj) + : ::sfx2::SvBaseLink(::SfxLinkUpdateMode::ONCALL, SotClipboardFormatId::SIMPLE_FILE) + , mrMediaObj(rObj) +{ + SetSynchron(false); +} + +::sfx2::SvBaseLink::UpdateResult SdrMediaLink::DataChanged( + const OUString&, const css::uno::Any&) +{ + // Reached only after a link update was allowed, so drop the placeholder + // and fetch a fresh snapshot now, like a linked graphic refetches. + mrMediaObj.m_xImpl->m_xCachedSnapshot.clear(); +#if HAVE_FEATURE_AVMEDIA + mrMediaObj.m_xImpl->m_xPlayerListener.clear(); + OUString aRealURL = mrMediaObj.m_xImpl->m_MediaProperties.getTempURL(); + if (aRealURL.isEmpty()) + aRealURL = mrMediaObj.m_xImpl->m_MediaProperties.getURL(); + if (!aRealURL.isEmpty()) + mrMediaObj.grabSnapshot(aRealURL); +#endif + mrMediaObj.ActionChanged(); + return SUCCESS; +} + +void SdrMediaLink::Closed() +{ + mrMediaObj.m_xImpl->m_pMediaLink = nullptr; + SvBaseLink::Closed(); +} + SdrMediaObj::SdrMediaObj(SdrModel& rSdrModel) : SdrRectObj(rSdrModel) ,m_xImpl( new Impl ) @@ -91,6 +144,7 @@ SdrMediaObj::SdrMediaObj( SdrMediaObj::~SdrMediaObj() { + ImpDeregisterLink(); } bool SdrMediaObj::HasTextEdit() const @@ -182,26 +236,47 @@ uno::Reference< graphic::XGraphic > const & SdrMediaObj::getSnapshot() const OUString aRealURL = m_xImpl->m_MediaProperties.getTempURL(); if( aRealURL.isEmpty() ) + { + // No extracted copy inside the document, so the media is reached + // through its URL. Treat it like a linked graphic and only fetch + // once the user has allowed link updates, so opening or converting + // a document never silently fetches the referenced content. A + // model with no document shell has no user to ask, so it does not + // fetch either. + sfx2::LinkManager* pLinkManager(getSdrModelFromSdrObject().GetLinkManager()); + SfxObjectShell* pShell = pLinkManager ? pLinkManager->GetPersist() : nullptr; + if (!pShell || !pShell->getEmbeddedObjectContainer().getUserAllowsLinkUpdate()) + return m_xImpl->m_xCachedSnapshot; aRealURL = m_xImpl->m_MediaProperties.getURL(); - OUString sReferer = m_xImpl->m_MediaProperties.getReferer(); - OUString sMimeType = m_xImpl->m_MediaProperties.getMimeType(); - uno::Reference xCachedSnapshot = m_xImpl->m_xCachedSnapshot; - - m_xImpl->m_xPlayerListener.set(new avmedia::PlayerListener( - [this, xCachedSnapshot, aRealURL, sReferer, sMimeType](const css::uno::Reference& rPlayer){ - SolarMutexGuard g; - uno::Reference xGraphic - = m_xImpl->m_MediaProperties.getGraphic().GetXGraphic(); - m_xImpl->m_xCachedSnapshot = avmedia::MediaWindow::grabFrame(rPlayer, xGraphic); - ActionChanged(); - })); - - avmedia::MediaWindow::grabFrame(aRealURL, sReferer, sMimeType, m_xImpl->m_xPlayerListener); + } + grabSnapshot(aRealURL); } #endif return m_xImpl->m_xCachedSnapshot; } +void SdrMediaObj::grabSnapshot(const OUString& rRealURL) const +{ +#if HAVE_FEATURE_AVMEDIA + OUString sReferer = m_xImpl->m_MediaProperties.getReferer(); + OUString sMimeType = m_xImpl->m_MediaProperties.getMimeType(); + uno::Reference xCachedSnapshot = m_xImpl->m_xCachedSnapshot; + + m_xImpl->m_xPlayerListener.set(new avmedia::PlayerListener( + [this, xCachedSnapshot, rRealURL, sReferer, sMimeType](const css::uno::Reference& rPlayer){ + SolarMutexGuard g; + uno::Reference xGraphic + = m_xImpl->m_MediaProperties.getGraphic().GetXGraphic(); + m_xImpl->m_xCachedSnapshot = avmedia::MediaWindow::grabFrame(rPlayer, xGraphic); + ActionChanged(); + })); + + avmedia::MediaWindow::grabFrame(rRealURL, sReferer, sMimeType, m_xImpl->m_xPlayerListener); +#else + (void)rRealURL; +#endif +} + void SdrMediaObj::AdjustToMaxRect( const tools::Rectangle& rMaxRect, bool bShrinkOnly /* = false */ ) { Size aSize( Application::GetDefaultDevice()->PixelToLogic( @@ -400,6 +475,8 @@ void SdrMediaObj::mediaPropertiesChanged( const ::avmedia::MediaItem& rNewProper { m_xImpl->m_xCachedSnapshot.clear(); m_xImpl->m_xPlayerListener.clear(); + // the URL is changing, so any link registered for the old one is stale + ImpDeregisterLink(); m_xImpl->m_MediaProperties.setFallbackURL( rNewProperties.getFallbackURL() ); OUString const& url(rNewProperties.getURL()); if (url.startsWithIgnoreAsciiCase("vnd.sun.star.Package:")) @@ -441,6 +518,7 @@ void SdrMediaObj::mediaPropertiesChanged( const ::avmedia::MediaItem& rNewProper m_xImpl->m_pTempFile.reset(); m_xImpl->m_MediaProperties.setURL(url, u""_ustr, rNewProperties.getReferer()); } + ImpRegisterLink(); bBroadcastChanged = true; } @@ -466,4 +544,52 @@ void SdrMediaObj::mediaPropertiesChanged( const ::avmedia::MediaItem& rNewProper } } +void SdrMediaObj::ImpRegisterLink() +{ + sfx2::LinkManager* pLinkManager(getSdrModelFromSdrObject().GetLinkManager()); + if (!pLinkManager || m_xImpl->m_pMediaLink) + return; + +#if HAVE_FEATURE_AVMEDIA + // Media copied into the document is extracted to a temporary file and is + // not a link. Only an external reference gets registered. + if (m_xImpl->m_pTempFile) + return; +#endif + const OUString& rURL = m_xImpl->m_MediaProperties.getURL(); + if (rURL.isEmpty() || rURL.startsWithIgnoreAsciiCase("vnd.sun.star.Package:")) + return; + + // Registration happens even in a build without media playback, so the + // external reference stays visible to the user as a document link. + m_xImpl->m_pMediaLink = new SdrMediaLink(*this); + pLinkManager->InsertFileLink(*m_xImpl->m_pMediaLink, + sfx2::SvBaseLinkObjectType::ClientFile, rURL); +} + +void SdrMediaObj::ImpDeregisterLink() +{ + sfx2::LinkManager* pLinkManager(getSdrModelFromSdrObject().GetLinkManager()); + if (pLinkManager && m_xImpl->m_pMediaLink) + { + // Remove implicitly deletes the link object + pLinkManager->Remove(m_xImpl->m_pMediaLink); + m_xImpl->m_pMediaLink = nullptr; + } +} + +void SdrMediaObj::handlePageChange(SdrPage* pOldPage, SdrPage* pNewPage) +{ + const bool bRemove(pNewPage == nullptr && pOldPage != nullptr); + const bool bInsert(pNewPage != nullptr && pOldPage == nullptr); + + if (bRemove) + ImpDeregisterLink(); + + SdrRectObj::handlePageChange(pOldPage, pNewPage); + + if (bInsert) + ImpRegisterLink(); +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab: */